
/*
XPT2046 / AD7843 Touch Screen Driver for PIC32MX795F512L
Copyright (C) 2012 Craig Dawson, Aaron Mohtar, Craig Peacock

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <p32xxxx.h>
#include "XPT2046.h"
#include "HardwareProfile.h"
#include "GOL.h"
#include "math.h"

// Touch screen calibration coefficients
double ax = 58;
double bx = -11;

double ay = -7207;
double by = 7276;

// Stores the last x & y coordinates successfully sensed
unsigned int tsX = 0;
unsigned int tsY = 0;
int tsPressure = 0;

// Timer3 counter
unsigned int tmr3Counter = 0;

// Unique user defined IDs
unsigned int st1ID = 100;

void touchScrInit(unsigned char EnableInterrupt) {
    TS_CS_DIR = 0;
    TS_CS = 1;

    TRISAbits.TRISA15 = 1;
    SPI4CONbits.SMP = 0;        // Input data sampled at middle of data output time
    SPI4CONbits.CKP = 0;        // Idle state of clock is low
    SPI4CONbits.CKE = 1;        // Data output changes on transition from active to idle
    SPI4CONbits.MSTEN = 1;      // Master mode
    SPI4BRG = 31;               // 1.25 MHz
    SPI4CONbits.ON = 1;         // Finally turn on SPI peripheral

    if (EnableInterrupt) {
        INTCONbits.INT4EP = 0;  // Interrupt on falling edge??
        IPC4bits.INT4IP = 3;    // Priority 3
        IPC4bits.INT4IS = 3;    // Subpriority

        // Send command to XPT2046 to enable interrupts
        touchScrSPICommand(TS_SAMP_X);

        IFS0bits.INT4IF = 0;    // Clear INT4 Flag;
        IEC0bits.INT4IE = 1;    // Enable INT4 Interrupt;

        // Enable timer 3 to continuously poll the touchscreen once it is pressed.
        // The interrupt for timer 3 is not enabled here
        OpenTimer3(T3_ON | T3_PS_1_256 | T3_SOURCE_INT, (FCY/256)/100);
        SetPriorityIntT3(T3_INT_PRIOR_3);
    }
}

unsigned int touchScrSPICommand(unsigned int command) {

    unsigned char byte1;
    unsigned char byte2;

    TS_CS = 0;

    SPI4BUF = command;  // Send Command
    while (!SPI4STATbits.SPIRBF);
    byte1 = SPI4BUF;    // Read Dummy

    SPI4BUF = 0x00;     // Send Dummy
    while (!SPI4STATbits.SPIRBF);
    byte1 = SPI4BUF;    // Read Byte 1

    SPI4BUF = 0x00;     // Send Dummy
    while (!SPI4STATbits.SPIRBF);
    byte2 = SPI4BUF;    // Read Byte 2

    TS_CS = 1;

    return ((byte1 << 5) + (byte2 >> 3));
}

unsigned char touchScrGetCord(unsigned int * scaledX, unsigned int * scaledY, signed int * pressure) {

    int x = 0;
    int y = 0;
    int Z1 = 0;
    int Z2 = 0;
    int i;

    // Get average over 5 samples.
    for (i = 0; i < 5; i++) {
        y += touchScrSPICommand(TS_SAMP_Y);
        x += touchScrSPICommand(TS_SAMP_X);

        Z1 += touchScrSPICommand(TS_SAMP_Z1);
        Z2 += touchScrSPICommand(TS_SAMP_Z2);
    }

    x /= 5;
    y /= 5;
    Z1 /= 5;
    Z2 /= 5;

    if (Z1 != 0)
    {
        *pressure = x * ((float) Z2 / Z1 - 1)/100;
        *pressure = 10/log10(*pressure);
        if(*pressure < 4)
            *pressure = 0;
        else
            *pressure -= 4;
    }
    else
        *pressure = 0;

    if (y) {

        // Scale Y to pixel value
        y = ay * y + by;

        if (y < 0) y = 0;
        else if (y >= 320) y = 319;

        // Scale X to pixel value
        x = (ax * x + bx); 

        if (x < 0) x = 0;
        else if (x >= 240) x = 239;

        *scaledY = y;
        *scaledX = x;
        // Pen pressed and cordinates calculated
        return (1);
    } else {
        return (0); // Screen not pressed
    }
}

void touchScrCalibrate() {

    unsigned int X1 = 100;
    unsigned int Y1 = 0;

    unsigned int X2 = 0;
    unsigned int Y2 = 0;
    unsigned int temp = 0;

    touchScrInterrupt(DISABLE);

    delayMs(50);

    SetColor(BLACK);

    STATICTEXT *st;
    st = StCreate(st1ID, 30, 145, 200, 170, ST_DRAW, "Touch the circles", NULL);
    GOLDraw();

    delayMs(500);

    SetColor(WHITE);
    Circle(10, 10, 8);
    delayUs(1);

    do {
        X1 = touchScrSPICommand(TS_SAMP_X);
        delayMs(10);
    } while (X1 < 200);

    for (temp = 0; temp < 10; temp++) {
        Y1 += touchScrSPICommand(TS_SAMP_Y);
        delayUs(1);
        X1 += touchScrSPICommand(TS_SAMP_X);
        delayUs(1);
    }

    X1 = X1 / 10;
    Y1 = Y1 / 10;

    do {
        temp = touchScrSPICommand(TS_SAMP_X);
    } while (temp > 100);

    SetColor(BLACK);
    Bar(0, 0, 20, 20);

    delayMs(1000);

    SetColor(WHITE);
    Circle(229, 309, 8);

    do {
        X2 = touchScrSPICommand(TS_SAMP_X);
        delayMs(10);
    } while (X2 < 100);

    for (temp = 0; temp < 10; temp++) {
        Y2 += touchScrSPICommand(TS_SAMP_Y);
        delayUs(1);
        X2 += touchScrSPICommand(TS_SAMP_X);
        delayUs(1);
    }

    X2 = X2 / 10;
    Y2 = Y2 / 10;

    ax = (double) (229 - 10) / (X2 - X1);
    bx = 230 - ax*X2;

    ay = (double) (309 - 10) / ((double) Y2 - Y1);
    by = 310 - ay*Y2;

    SetColor(BLACK);
    Bar(220, 300, 239, 319);

    touchScrInterrupt(ENABLE);
}

void __attribute((interrupt(ipl3), vector(_EXTERNAL_4_VECTOR), nomips16)) _TouchScreenHandler(void) {

    // This interrupt occurs when the stylus first touches the screen

    unsigned int x, y;
    int pressure;

    touchScrInterrupt(DISABLE);

    touchScrGetCord(&x, &y, &pressure);

    if (x > 0) {

        // Create message to send to Graphic Object Layer (GOL)
        GOL_MSG msg;
        msg.type    = TYPE_TOUCHSCREEN;
        msg.uiEvent = EVENT_PRESS;
        msg.param1  = x;
        msg.param2  = y;

        tsX = x;
        tsY = y;
        tsPressure = pressure;

        GOLMsg(&msg);

        // Enable timer3 to follow the stylus.
        tmr3Interrupt(ENABLE);
        touchScrInterrupt(DISABLE);
    } else
        touchScrInterrupt(ENABLE);

    delayUs(10);
}

void __attribute((interrupt(ipl3), vector(_TIMER_3_VECTOR), nomips16)) _Timer3Handler(void) {

    // This interrupt occurs at predefined intervals while the stylus is in contact with the screen.

    IFS0bits.T3IF = 0;

    if(tmr3Counter++ < TMR3DELAY)
        return;

    tmr3Counter = 0;

    tmr3Interrupt(DISABLE);
    delayUs(10);

    unsigned int x, y, pressure;
    touchScrGetCord(&x, &y, &pressure);

    GOL_MSG msg;
    msg.type = TYPE_TOUCHSCREEN;

    if (x > 0) {
        // Send message to GOL to update coordinates
        touchScrInterrupt(DISABLE);

        msg.uiEvent = EVENT_STILLPRESS;
        msg.param1 = x;
        msg.param2 = y;

        tsX = x;
        tsY = y;
        tsPressure = pressure;

        GOLMsg(&msg);

        tmr3Interrupt(ENABLE);
    } else {
        // Send message to GOL to indicate stylus has been released
        msg.uiEvent = EVENT_RELEASE;
        msg.param1 = tsX;
        msg.param2 = tsY;

        GOLMsg(&msg);
        tmr3Interrupt(DISABLE);

        touchScrInterrupt(ENABLE);
    }
}


